/*
|-----------------------------------------------------------------------------|
| Overall ANTLR Grammar File Structure                                        |
|-----------------------------------------------------------------------------|
| «grammarType» grammar name;
|   «optionsSpec»     = options { ... }
|   «tokensSpec»      = tokens { ... }
|   «attributeScopes» = «@action-scope-name::action-name» { ... }
|   «actions»
|   // rules
|   rule1 : ... ;
|   rule2 : ... ;
|   ...
|_____________________________________________________________________________|
|   «grammarType»         = ( lexer | parser | tree | <empty> )
|   «optionsSpec»         = options { ... }
|   «tokensSpec»          = tokens { ... }
|   «attributeScopes»     = 
|   «actions»             = @«action-scope-name»::«action-name» { ... }
|    «action-scope-name» = ( lexer | parser | tree | <empty> = parser )
|    «action-name»       = ( header | members | rulecatch | synpredgate )
|-----------------------------------------------------------------------------|
*/
grammar Flex;

  options {
    backtrack=true;
    language = Java;
    output = AST;
  }// options
  
  @lexer::header {
    package pl.p05;
  }//header
  @lexer::members {
  }// members
 
  @parser::header {
    package pl.p05;
  }//header
  @parser::members {
    String name;
  }// members

//= Grammar Rules FLEX =======================================================|
rulesFlex
  : flexDefinitions
    BlockDivider
    flexRules
    BlockDivider
    codeC
    EOF
  ;
/* --- flexDefinitions---*/
  flexDefinitions
    : (name (pattern)+)* (copyBlock)?
    ;
  
    name
      : Identifier
      ;
    
    pattern
      : regularExpresion ( Stick regularExpresion)*
      ;
      
      regularExpresion
        : range
        | set
        | blockParenthesis
        | StringLiteral regularOperations?
        | CharacterLiteral regularOperations?
        ;
        
        range
          : LeftSquareBrackets 
              ( Range )+
            RightSquareBrackets regularOperations?
          ;
          
        set
          : LeftBracket
              name
            RightBracket regularOperations?
          ;
          
        blockParenthesis
          : LeftParenthesis
              pattern
            RightParenthesis regularOperations?
          ;
  
        regularOperations
          : Asterisk
          | Plus
          | Question
          ;
  
  copyBlock: BeginCopyBlock rulesC EndCopyBlock;
  
/* --- flexRules --- */
  flexRules
    : ( (pattern)+ action)+
    ;

    action
      : (statement)*
      ;

/* --- flexRules --- */
  codeC
    : rulesC
    ;
//= Grammar Rules C ==========================================================|
rulesC
  : (declaration)*
  ;

/** In this rule, the functionHeader left prefix on the last two
*  alternatives is not LL(k) for a fixed k.  However, it is
*  LL(*).  The LL(*) algorithm simply scans ahead until it sees
*  either the SemiColon or the '{' of the block and then it picks
*  the appropriate alternative.  Lookhead can be arbitrarily
*  long in theory, but is <=10 in most cases.  Works great.
*  Use ANTLRWorks to see the lookahead use (step by Location)
*  and look for blue tokens in the input window pane. :)
*/
  declaration
    : variable
    | functionHeader SemiColon {System.out.println($functionHeader.name + " is a prototype");}
    | functionHeader block {System.out.println($functionHeader.name + " is a definition");}
    ;

    variable
      : type declarator SemiColon
      ;
  
      type
        : Int   
        | Char  
        | Void
        | Float
        | Identifier
        ;
    
      declarator
        : Identifier 
        ;

    functionHeader returns [String name]
    @init {
      name=null; // for now you must init here rather than in 'returns'
    }
      : type Identifier LeftParenthesis ( formalParameter ( Comma formalParameter )* )? RightParenthesis
      { $name = $Identifier.text; }
      ;

    functionCall returns [String name]
    @init {
      name=null; // for now you must init here rather than in 'returns'
    }
      : Identifier LeftParenthesis ( expression ( Comma expression )* )? RightParenthesis
      { $name = $Identifier.text; }
      ;

      formalParameter
        : type declarator
        ;

    block
      : LeftBracket
          (statement)*
          (returnRule SemiColon)?
        RightBracket
      ;
      returnRule
        : Return expression 
        ;
  
    statement
      : variable
      | functionCall SemiColon {System.out.println($functionCall.name + " is a invocation");}
      | forStatement
      | ifStatement
      | whileStatement
      | doWhileStatement
      | expression SemiColon      
      | block
      | assignStatement SemiColon
      | SemiColon
      ;
    
      forStatement
        : For LeftParenthesis assignStatement SemiColon expression SemiColon assignStatement RightParenthesis block        
        ;
      
      ifStatement
        : If LeftParenthesis expression RightParenthesis block (Else block)?
        ;
        
      whileStatement
        : While LeftParenthesis expression RightParenthesis block
        ;
        
      doWhileStatement
        : Do block whileStatement
        ;
    
      assignStatement
        : Identifier Assing (expression | functionCall {System.out.println($functionCall.name + " is a invocatiotion");} )        
        ;
    
      expression
        : simpleExpression ( relacionalOperator simpleExpression)?
        | True
        | False
        ;
      
        simpleExpression
          : (Plus|Minus)? term ( (Plus|Minus) term )*
          ;
        
          term
            : factor ( (Asterisk|Slash) factor)*
            ;
            
            factor
              : Not factor
              | Identifier
              | Identifier Plus Plus      
              | DecimalLiteral      
              | StringLiteral
              | CharacterLiteral
              | LeftParenthesis expression RightParenthesis
              ;
    
        relacionalOperator
          : Not
          | Equal
          | NotEqual
          | Ampersand Ampersand
          | Stick Stick
          | Less
          | Greater
          ;

//= Lexer Rules FLEX =========================================================|
  BlockDivider: '%%';
  BeginCopyBlock: '%{';
  EndCopyBlock: '%}';
//= Lexer Rules C ============================================================|
   /* Tipos de datos */
   Void  : 'void';
   Int   : 'int';
   Char  : 'char';
   Float : 'float';

   /* Palabras reservadas */
   If    : 'if';
   Else  : 'else';
   While : 'while';
   For   : 'for';
   Do    : 'do';
   True  : 'true';
   False : 'false';
   Return: 'return';    

  /* Nueva línea */
  fragment NewLine: '\r'? '\n';

  /* Espacios */
  WhiteSpace : (' '|'\r'|'\t'|'\u000C'|'\n') { $channel=HIDDEN; skip(); };

  /* Dígitos */
  fragment Digit: ('0'..'9') ;
  fragment HexDigit : (Digit|'a'..'f'|'A'..'F') ;
  fragment OctalDigit : ('0'..'7') ;

  /* Números */
  HexLiteral : '0' ('x'|'X') HexDigit+ ;
  DecimalLiteral : ('0' | '1'..'9' Digit*) ;
  OctalLiteral : '0' (OctalDigit)+ ;
  fragment Exponent : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;
  FloatingPointLiteral :   (Digit)+ '.' (Digit)* Exponent? ;

  /* Letras */
  fragment LetterLower: ('a'..'z');
  fragment LetterUpper: ('A'..'Z');
  fragment Letter: (LetterLower|LetterUpper|'_');

  /* Identificadores */
  Identifier: Letter (Letter | Digit)*;

  /* Cadenas */
  fragment EscapeSequence
    :   '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
    |   OctalEscape
    ;
  fragment OctalEscape
    :   '\\' ('0'..'3') OctalDigit OctalDigit
    |   '\\' OctalDigit OctalDigit
    |   '\\' OctalDigit
    ;
  CharacterLiteral: '\'' ( EscapeSequence | ~('\''|'\\') ) '\'' ;
  StringLiteral: '"' (options {greedy=false;}: (EscapeSequence|~('\\'|'"')) )* '"';
  Range
    : Letter Minus Letter
    | Digit Minus Digit
    ;

  /* Separadores */
  LeftParenthesis     : '(';
  RightParenthesis    : ')';
  LeftBracket         : '{';
  RightBracket        : '}';
  LeftSquareBrackets  : '[';
  RightSquareBrackets : ']';
  Colon               : ':';
  SemiColon           : ';';
  Comma               : ',';

  /* Operadores */
  Not       : '!';
  Equal     : '==';
  NotEqual  : '!=';
  Less      : '<';
  Greater   : '>';
  Assing    : '=';
  Plus      : '+';
  Minus     : '-';
  Asterisk  : '*';
  Ampersand : '&';
  Stick     : '|';
  Slash     : '/';
  Question  : '?';

  /* Comentarios */
  CommentLine
    : '//' ~('\n'|'\r')* NewLine  { $channel=HIDDEN; skip(); } 
    ;
  NestedCommentBlock
    : '/*' (options {greedy=false;} : (NestedCommentBlock | .))* '*/'
      { $channel=HIDDEN; skip(); }
    ;
  CommandLine
      : '#' (options {greedy=false;} : ~('\n'|'\r'))* NewLine { $channel=HIDDEN; skip(); }
      ;
